package com.koron.plan.web.maintainplan.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageInfo;
import com.koron.bean.base.Response;
import com.koron.bean.enums.TimePeriod;
import com.koron.bean.query.PageQuery;
import com.koron.bean.standard.jobStock.maintain.vo.MaintainVO;
import com.koron.bean.system.staff.vo.EamUser;
import com.koron.bean.workflow.RedirectQuery;
import com.koron.common.core.config.ThreadLocalContext;
import com.koron.common.core.util.CodeTools;
import com.koron.common.core.util.OrgCacheUtils;
import com.koron.common.task.web.entity.CronTask;
import com.koron.common.task.web.entity.XxlJobInfo;
import com.koron.common.task.web.service.XxlJobService;
import com.koron.plan.config.Constants;
import com.koron.plan.web.common.CommonUtils;
import com.koron.plan.web.feign.OrderFeign;
import com.koron.plan.web.maintainplan.bean.convertor.MaintainConvertor;
import com.koron.plan.web.maintainplan.bean.convertor.MaintainPlanDispatchConvertor;
import com.koron.plan.web.maintainplan.bean.convertor.MaintainPlanStandardConvertor;
import com.koron.plan.web.maintainplan.bean.dto.MaintainDTO;
import com.koron.plan.web.maintainplan.bean.dto.OrderDispatchDto;
import com.koron.plan.web.maintainplan.bean.entity.*;
import com.koron.plan.web.maintainplan.bean.query.MaintainPlanQuery;
import com.koron.plan.web.maintainplan.bean.vo.MaintainPlanDetailVO;
import com.koron.plan.web.maintainplan.bean.vo.MaintainPlanDispatchDetailVO;
import com.koron.plan.web.maintainplan.bean.vo.MaintainPlanStandardVO;
import com.koron.plan.web.maintainplan.bean.vo.MaintainPlanVO;
import com.koron.plan.web.maintainplan.mapper.MaintainPlanMapper;
import com.koron.plan.web.maintainplan.service.*;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author twcao
 * @title: MaintainPlanServiceImpl
 * @projectName GDH_EAM_V2
 * @description: 维保计划service
 * @date 2021/8/2710:00
 */
@Slf4j
@Service
public class MaintainPlanServiceImpl implements MaintainPlanService {

    @Autowired
    private MaintainPlanReadyService readyService;

    @Autowired
    private MaintainPlanStandardService standardService;

    @Autowired
    private MaintainPlanInstructionService instructionService;

    @Autowired
    private MaintainPlanDispatchService dispatchService;

    @Autowired
    private MaintainPlanDispatchWorkerService dispatchWorkerService;

    @Autowired
    private MaintainPlanDispatchDeptService workDeptService;

    @Autowired
    private PlanEquipService planEquipService;

    @Autowired
    private XxlJobService xxlJobService;

    @Autowired
    private MaintainPlanStandardConvertor standardConvertor;

    @Autowired
    private MaintainPlanDispatchConvertor dispatchConvertor;

    @Autowired
    private MaintainConvertor maintainConvertor;

    @Autowired
    private OrgCacheUtils orgCacheUtils;

    @Autowired
    private OrderFeign orderFeign;
    @Autowired
    private HttpServletRequest httpServletRequest;
    /**
     * 下发维保计划，生成工单
     * @param factory 会话
     * @param id      维保计划id
     * @return
     */
    @Override
    @TaskAnnotation("issue")
    public MaintainPlanDetailVO issue(SessionFactory factory, String id) {
        MaintainPlanMapper mapper = factory.getMapper(MaintainPlanMapper.class);
        MaintainPlanDetailVO detailVO = mapper.queryDetailById(id,httpServletRequest.getHeader("Language"));
        Assert.isTrue(Constants.MAINTAIN_PLAN_STATE.USING.equals(detailVO.getStatus()), "计划处于草稿状态，无法下发");
        log.info("手动下发维保计划，生成工单");
        detailVO.setDeptManageName(orgCacheUtils.getOrgNameById(detailVO.getDeptManage()));
        voWrapper(detailVO);
        boolean flag = generateRepairOrder(detailVO);
        Assert.isTrue(flag, "维保计划生成失败");
        return detailVO;
    }

    /**
     * 启用或关闭维保计划
     * @param factory     会话
     * @param id          维保计划id
     * @param startOrStop 启动或停止标志，startOrStop = true，启用维保计划(已启用)。startOrStop = false，停用维保计划(草稿)
     * @return 修改后的详情
     */
    @Override
    @TaskAnnotation("startOrStop")
    public MaintainPlanDetailVO startOrStop(SessionFactory factory, String id, Boolean startOrStop) {
        MaintainPlanMapper mapper = factory.getMapper(MaintainPlanMapper.class);
        MaintainPlanDetailVO detailVO = queryById(factory, id, null);
        if(Constants.YesOrNo.YES.equals(detailVO.getIsTimer())) {
            if(startOrStop) {
                List<String> executeTimes = xxlJobService.nextTriggerTime(detailVO.getCronTask().getCron(), 2);
                Assert.notEmpty(executeTimes, "定时任务设定的时间有误,请保存后重试");
                // 已启用，且定时下达
                if(!CommonUtils.isPositive(detailVO.getDelayDays())) {
                    // 1、不提前 ---> 启动定时任务
                    xxlJobService.start(id);
                } else {
                    xxlJobService.start(id);
                    // 2、提前 ---> 创建一个定点执行的任务（只执行一次的任务）
                    Assert.isTrue(Constants.MAINTAIN_PLAN_STATE.DRAFT.equals(detailVO.getStatus()), "任务已处于启用状态");
                    Date firstTaskTime = getFirstExecuteTimes(detailVO.getCronTask().getCron(), detailVO.getDelayDays());
                    String executeTCron = CommonUtils.getCron(firstTaskTime);
                    CronTask cronTask = xxlJobService.getCronTaskById(id);
                    XxlJobInfo jobInfo = xxlJobService.getXxlJobInfoById(cronTask.getXxlJobId());
                    xxlJobService.addSimpleJob(String.valueOf(cronTask.getXxlJobId()), executeTCron, cronTask.getMark(), "jobId=" + cronTask.getXxlJobId() + "的临时一次性任务", detailVO.getUpdateBy(), jobInfo.getExecutorHandler(), 30);
                    xxlJobService.start(String.valueOf(cronTask.getXxlJobId()));
                }
                detailVO.setStatus(Constants.MAINTAIN_PLAN_STATE.USING);
            } else {
                Assert.isTrue(Constants.MAINTAIN_PLAN_STATE.USING.equals(detailVO.getStatus()), "任务已处于停用状态");
                if(!CommonUtils.isPositive(detailVO.getDelayDays())) {
                    // 1、不提前， 停止任务
                    xxlJobService.pause(id);
                } else {
                    CronTask cronTask = xxlJobService.getCronTaskById(id);
                    if(cronTask != null && cronTask.getXxlJobId() != null) {
                        xxlJobService.remove(String.valueOf(cronTask.getXxlJobId()));
                    }
                }
                // 如果是试验计划关闭后禁止重新启动；
                if (StrUtil.startWith(detailVO.getCode(),Constants.EXPERIMENT_PLAN_PREFIX)){
                    detailVO.setStatus(Constants.MAINTAIN_PLAN_STATE.DISABLED);
                }else {
                    detailVO.setStatus(Constants.MAINTAIN_PLAN_STATE.DRAFT);
                    // 设置新的流程业务id
                    detailVO.setBillId(CodeTools.getCode32());
                }
            }
        } else {
            if (StrUtil.startWith(detailVO.getCode(),Constants.EXPERIMENT_PLAN_PREFIX)){
                detailVO.setStatus(startOrStop ? Constants.MAINTAIN_PLAN_STATE.USING : Constants.MAINTAIN_PLAN_STATE.DISABLED);
            }else {
                detailVO.setStatus(startOrStop ? Constants.MAINTAIN_PLAN_STATE.USING : Constants.MAINTAIN_PLAN_STATE.DRAFT);
            }
        }
        mapper.saveOrUpdate(detailVO);
        return detailVO;
    }

    /**
     * 通过ID查询单条数据
     * @param factory 会话
     * @param id 主键
     * @return 实例对象
     */
    @Override
    @TaskAnnotation("queryById")
    public MaintainPlanDetailVO queryById(SessionFactory factory, String id, String language) {
        MaintainPlanMapper mapper = factory.getMapper(MaintainPlanMapper.class);
        MaintainPlanDetailVO detailVO = mapper.queryDetailById(id,language);
        if (!CollectionUtils.isEmpty(detailVO.getStandards())){
            detailVO.getStandards().stream().forEach(maintainPlanStandardVO -> {
                String langValue = mapper.getLangValue(maintainPlanStandardVO.getEquipmentId(), language);
                if (StringUtils.isNotBlank(langValue)){
                    maintainPlanStandardVO.setEquipmentName(langValue);
                }
            });
        }
        voWrapper(detailVO);
        return detailVO;
    }

    @Override
    @TaskAnnotation("queryByPlanId")
    public PageInfo<List<MaintainVO>>  queryByPlanId(SessionFactory factory, String planId, PageQuery pageQuery) {
        MaintainPlanQuery maintainPlanQuery = new MaintainPlanQuery();
        maintainPlanQuery.setPlanId(planId);
        Response<PageInfo> response = orderFeign.queryByPlanId(planId,pageQuery.getPage(),pageQuery.getPageSize());
        PageInfo<List<MaintainVO>> result = (PageInfo<List<MaintainVO>>) response.getData();
        return result;
    }

    /**
     * 维保计划分页查询(默认只查未被删除的)
     * @param bean 分页查询的条件
     * @return 结果集合
     */
    @Override
    @TaskAnnotation("queryList")
    public List<MaintainPlanVO> queryList(SessionFactory factory, MaintainPlanQuery bean) {
        MaintainPlanMapper mapper = factory.getMapper(MaintainPlanMapper.class);
        bean.setDeleteFlag(bean.getDeleteFlag() == null ? false : true);
        List<MaintainPlanVO> maintainPlanBeans = mapper.queryList(bean);
        maintainPlanBeans.stream().forEach(row -> voWrapper(row));
        return maintainPlanBeans;
    }

    /**
     * 新增或更新
     * @param factory 会话
     * @param detailVO 保存的实体
     * @return 保存成功后的实体
     */
    @Override
    @TaskAnnotation("saveOrUpdate")
    public MaintainPlanDetailVO saveOrUpdate(SessionFactory factory, MaintainPlanDetailVO detailVO) {
        // 1、初始化
        // 1.1、设置主键
        detailVO.setId(StringUtils.isBlank(detailVO.getId()) ? CodeTools.getCode32() : detailVO.getId());
        // 1.2、设置删除标志
        CommonUtils.update(detailVO);
        // 1.3、生成流程业务id
        detailVO.setBillId(StringUtils.isBlank(detailVO.getBillId()) ? CodeTools.getCode32() : detailVO.getBillId());

        // 设置cron表达式
        if(Constants.YesOrNo.YES.equals(detailVO.getIsTimer())) {
            detailVO.getCronTask().setCron(CommonUtils.parseStrToCron(detailVO.getCronTask()));
        }
        // 1.3、设置计划状态， 默认为草稿状态
        detailVO.setStatus(detailVO.getStatus() == null ? Constants.MAINTAIN_PLAN_STATE.DRAFT : detailVO.getStatus());
        detailVO.setCode(StringUtils.isBlank(detailVO.getCode()) ? CommonUtils.getCodeNo(Constants.MAINTAIN_PLAN_PREFIX, 3) : detailVO.getCode());
        // 2、参数合法性校验
        // 2.1、只有草稿状态的维保计划才可以修改
        // Assert.isTrue(Constants.MAINTAIN_PLAN_STATE.DRAFT.equals(detailVO.getStatus()), "请先停用维保计划");
        // 3、维保计划基础
        MaintainPlanMapper planMapper = factory.getMapper(MaintainPlanMapper.class);
        detailVO.setDeptName(orgCacheUtils.getOrgNameById(detailVO.getDeptId()));
        detailVO.setDeptManageName(orgCacheUtils.getOrgNameById(detailVO.getDeptManage()));
        Assert.isTrue(planMapper.saveOrUpdate(detailVO), "维保计划保存失败");
        // 4、作业准备
        List<MaintainPlanReadyBean> worksReadys = detailVO.getWorkReadys();
        if(!CollectionUtils.isEmpty(worksReadys)) {
            // 设置排序值sort
            CommonUtils.sorted(worksReadys);
            worksReadys.stream().forEach(row -> row.setPlanId(detailVO.getId()));
            readyService.deleteByPlanId(factory, detailVO.getId());
            Assert.isTrue(readyService.saveOrUpdate(factory, worksReadys), "作业准备保存失败");
        }
        // 5、作业过程(作业标准)
        List<MaintainPlanStandardVO> standards = detailVO.getStandards();
        if(!CollectionUtils.isEmpty(standards)) {
            // 设置排序值sort
            CommonUtils.sorted(standards);
            standards.stream().forEach(row -> row.setPlanId(detailVO.getId()));
            standardService.deleteByPlanId(factory, detailVO.getId());
            Assert.isTrue(standardService.saveOrUpdate(factory, standardConvertor.vosToBeans(standards)), "作业过程保存失败");
        }
        // 6、作业指导书，如果为empty，则表明清空指导书，如果为null，则不做操作
        List<MaintainPlanInstructionBean> instructions = detailVO.getInstructions();
        if(instructions != null) {
            // 设置排序值sort
            CommonUtils.sorted(instructions);
            instructions.stream().forEach(row -> row.setPlanId(detailVO.getId()));
            instructionService.deleteByPlanId(factory, detailVO.getId());
            Assert.isTrue(instructionService.saveOrUpdate(factory, instructions), "作业指导书保存失败");
        }
        // 7、派工信息
        MaintainPlanDispatchDetailVO dispatch = detailVO.getDispatch();
        if(Constants.YesOrNo.YES.equals(detailVO.getIsDispatch())) { // 派工信息可为空
            Assert.notNull(dispatch, "派工信息不能为空");
            dispatch.setPlanId(detailVO.getId());
            dispatch.setId(StringUtils.isBlank(dispatch.getId()) ? CodeTools.getCode32() : dispatch.getId());
            dispatchService.deleteByPlanId(factory, detailVO.getId());
            Assert.isTrue(dispatchService.saveOrUpdate(factory, dispatchConvertor.detailVOToBean(detailVO.getDispatch())), "派工信息保存失败");
            // 7.1、工作班成员
            List<MaintainPlanDispatchWorkerBean> workers = dispatch.getWorkers();
            dispatchWorkerService.deleteByPlanId(factory, detailVO.getId());
            if(!CollectionUtils.isEmpty(workers)) {
                // 设置排序值sort
                CommonUtils.sorted(workers);
                workers.stream().forEach(row -> {
                    row.setPlanId(detailVO.getId());
                    row.setDispatchId(dispatch.getId());
                    row.setTeamId(CodeTools.getCode32());
                });
                Assert.isTrue(dispatchWorkerService.saveOrUpdate(factory, workers), "工作班组保存失败");
            }
            // 7.2、工作班工作单位
            List<MaintainPlanDispatchDept> workerDept = dispatch.getWorkerDept();
            workDeptService.deleteByPlanId(factory, detailVO.getId());
            if(!CollectionUtils.isEmpty(workerDept)) {
                CommonUtils.sorted(workerDept);
                workerDept.stream().forEach(row -> {
                    row.setPlanId(detailVO.getId());
                    row.setDispatchId(dispatch.getId());
                });
                Assert.isTrue(workDeptService.saveOrUpdate(factory, workerDept), "工作班组工作单位保存失败");
            }
        }
        // 8、设置定时任务表达式(detailVO.getIsTimer() = 1时，cron不能为空)
        if(Constants.YesOrNo.YES.equals(detailVO.getIsTimer())) {
            // 定时下达 = true，新增/更新任务
            Assert.notNull(detailVO.getCronTask(), "定时任务不能为空");
            List<String> executeTimesNum = xxlJobService.nextTriggerTime(detailVO.getCronTask().getCron(), 2);
            Assert.notEmpty(executeTimesNum, "请确认定时任务设定的时间,修改后保存重试");

            CronTask cronTask = xxlJobService.getCronTaskById(detailVO.getId());
            if(cronTask != null) {
                // 更新任务
                xxlJobService.update(detailVO.getId(), detailVO.getCronTask().getCron(), factory.getDbEnv(), null, detailVO.getUpdateBy(), null);
            } else {
                // 新增任务 创建人名字[时间]维保计划
                String jobName = "维保计划[" + detailVO.getCode() + "]";
                xxlJobService.addSimpleJob(detailVO.getId(), detailVO.getCronTask().getCron(), factory.getDbEnv(), jobName, detailVO.getUpdateBy(), "generateRepairOrder", 30);
            }
        } else {
            // 如果不是定时任务，则删除任务（之前为定时任务时，需要删除）
            xxlJobService.remove(detailVO.getId());
        }
        // 9、计划关联的设备
        List<PlanEquipment> equipments = detailVO.getEquipments();
        if(!CollectionUtils.isEmpty(equipments)) {
            // 如果关联了设备，则先删除原先的，再添加传入的
            planEquipService.deleteByPlanId(factory, detailVO.getId());
            equipments.stream().forEach(row -> row.setPlanId(detailVO.getId()));
            planEquipService.saveOrUpdate(factory, equipments);
        }
        return queryById(factory, detailVO.getId(), null);
    }

    /**
     * 通过主键删除数据（逻辑删除）
     * @param id 主键
     * @return 是否成功
     */
    @Override
    @TaskAnnotation("deleteById")
    public boolean deleteById(SessionFactory factory, String id) {
        MaintainPlanMapper mapper = factory.getMapper(MaintainPlanMapper.class);
        MaintainPlanDetailVO detailVO = mapper.queryDetailById(id,httpServletRequest.getHeader("Language"));
        detailVO.setDeleteFlag(true);
        // 1、维保计划基础信息
        mapper.saveOrUpdate(detailVO);
        // 2、工作准备
        List<MaintainPlanReadyBean> worksReady = detailVO.getWorkReadys();
        worksReady.stream().forEach(row -> row.setDeleteFlag(true));
        readyService.saveOrUpdate(factory, worksReady);
        // 3、作业标准
        List<MaintainPlanStandardVO> standards = detailVO.getStandards();
        standards.stream().forEach(row -> row.setDeleteFlag(true));
        standardService.saveOrUpdate(factory, standardConvertor.vosToBeans(standards));
        // 4、作业指导书
        List<MaintainPlanInstructionBean> instructions = detailVO.getInstructions();
        instructions.stream().forEach(row -> row.setDeleteFlag(true));
        instructionService.saveOrUpdate(factory, instructions);
        // 5、派工信息
        MaintainPlanDispatchDetailVO dispatch = detailVO.getDispatch();
        if(!Objects.isNull(dispatch)) {
            dispatch.setDeleteFlag(true);
            dispatchService.saveOrUpdate(factory, dispatch);
            // 6、工作班成员
            List<MaintainPlanDispatchWorkerBean> workers = dispatch.getWorkers();
            workers.stream().forEach(row -> row.setDeleteFlag(true));
            dispatchWorkerService.saveOrUpdate(factory, workers);
        }
        // 7、关联的设备
        planEquipService.deleteByPlanId(factory, detailVO.getId());
        // 8、定时任务
        xxlJobService.remove(detailVO.getId());
        return true;
    }

    /**
     * 生成日常维护工单
     */
    @XxlJob(value = "generateRepairOrder")
    public boolean generateRepairOrder(MaintainPlanDetailVO detailVO) {
        // 默认创建失败
        boolean flag = false;
        // 任务类型，1->手动任务，2->定时任务运，不提前，3->定时任务，提前
        Integer taskType = null;
        CronTask cronTask = null;
        // 1、获取任务详情
        if(detailVO != null) {
           // 1、手动生成
            taskType = 1;
        } else {
            // 2、定时任务
            try (SessionFactory factory = new SessionFactory()) {
                cronTask = xxlJobService.getCronTaskByXxlJobId((int) XxlJobHelper.getJobId());
                // 如果是定时任务，则获取的默认数据源为system，需要进行切换
                EamUser user = ThreadLocalContext.get();
                user = user == null ? new EamUser() : user;
                user.setCurrDs(cronTask.getMark());
                ThreadLocalContext.set(user);
                Assert.notNull(cronTask, "定时任务为空");
                if(CommonUtils.parse(cronTask.getBusinessId()) > 0) {
                    // 2.1、定时任务-提前（临时任务，将临时任务id作为业务id查询出定时任务）
                    XxlJobHelper.log("定时任务-提前");
                    taskType = 3;
                    cronTask = xxlJobService.getCronTaskByXxlJobId(CommonUtils.parse(cronTask.getBusinessId()));
                } else {
                    // 2.2、定时任务
                    XxlJobHelper.log("定时任务");
                    taskType = 2;
                }
                factory.setDbEnv(cronTask.getMark());
                XxlJobHelper.log("mark = {}", cronTask.getMark());
                detailVO = queryById(factory, cronTask.getBusinessId(), null);
                Assert.notNull(detailVO, "维保计划详情不能为空");
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
        log.info("维保计划详情: {}", JSON.toJSONString(detailVO));
        XxlJobHelper.log("维保计划定时任务类型： {}， 维保计划详情: {}", taskType == 1 ? "手动任务" : (taskType == 2 && detailVO.getDelayDays() == 0 ? "定时任务[不提前]" : "定时任务[提前]" + detailVO.getDelayDays() + "天"), JSON.toJSONString(detailVO));
        if(taskType == 1 || (taskType == 2 && !CommonUtils.isPositive(detailVO.getDelayDays())) || (taskType == 3 && CommonUtils.isPositive(detailVO.getDelayDays()))) {
            // 2、调用工单方法生成工单
            try {
                XxlJobHelper.log("定时任务调用工单方法生成工单,taskType:"+taskType);
                MaintainDTO maintainDTO = createMaintainOrder(detailVO, taskType);
                log.info("维保计划生成工单, dto = {}", JSON.toJSONString(maintainDTO));
                if(Constants.YesOrNo.YES.equals(detailVO.getIsDispatch())) {
                    // 如果isDispatch=true，则直接提交到"待派工"状态
                    maintainDTO.setIsCommit(true);
                }
                String orderId = CommonUtils.ok(orderFeign.insertOrUpdate(maintainDTO));
                flag = StringUtils.isBlank(orderId) ? false : true;
                XxlJobHelper.log("维护工单插入情况:"+flag);
                log.info("维保计划生成工单响应[派工 = {}], response = {}", maintainDTO.getIsCommit(), orderId);
                if(Constants.YesOrNo.YES.equals(detailVO.getIsDispatch())) {
                    // 如果isDispatch=true，保存派工信息，并提交到待执行状态
                    MaintainPlanDispatchDetailVO dispatch = detailVO.getDispatch();
                    OrderDispatchDto orderDispatchDto = maintainConvertor.dispatchToDispatchDTO(dispatch);
                    orderDispatchDto.setOrderId(orderId);
                    orderDispatchDto.setOrderType(Constants.OrderType.MAINTAIN);
                    if (dispatch.getIsOut() == 1){
                        orderDispatchDto.setLeader(dispatch.getTrackLeader());
                        orderDispatchDto.setLeaderName(dispatch.getTrackLeaderName());
                    }
                    orderDispatchDto.getOrderDispatchWorker().stream().forEach(row -> row.setOrderId(orderId));
                    orderDispatchDto.getOrderWorkDeptPoList().stream().forEach(row -> {
                        row.setOrderId(orderId);
                        row.setOrderType(Constants.OrderType.MAINTAIN);
                    });
                    // 正常维护工单，提交到"待执行"状态
                    orderDispatchDto.setIsCommit(true);
                    String id = CommonUtils.ok(orderFeign.saveDispatchInfo(orderDispatchDto));
                    flag = StringUtils.isBlank(id) ? false : true;
                    log.info("保存派工信息, response = {}", id);
                }
            } catch (Exception e) {
                XxlJobHelper.log(e.getMessage(), e);
                log.error(e.getMessage(), e);
                flag = false;
            }
        }
        // 3、如果是提前执行的任务，调用完工单后，删除当前任务，再生成下次执行的定点任务
        if(taskType == 3) {
            // 3.1、更新下次执行的定点任务
            Date firstTaskTime = getFirstExecuteTimes(detailVO.getCronTask().getCron(), detailVO.getDelayDays());
            String executeTCron = CommonUtils.getCron(firstTaskTime);
            XxlJobInfo jobInfo = xxlJobService.getXxlJobInfoById(cronTask.getXxlJobId());
            XxlJobHelper.log("jobInfo = {}, cronTask = {},detail = {}", JSON.toJSONString(jobInfo), JSON.toJSONString(cronTask), JSON.toJSONString(detailVO));
            xxlJobService.update(String.valueOf(cronTask.getXxlJobId()), executeTCron, cronTask.getMark(), "jobId=" + cronTask.getXxlJobId() + "的临时一次性任务", detailVO.getUpdateBy(), jobInfo.getExecutorHandler());
            xxlJobService.start(String.valueOf(cronTask.getXxlJobId()));
            XxlJobHelper.log("下一次临时一次性任务更新完成, 时间 = {}", executeTCron);
        }
        if(taskType == 2 && CommonUtils.isPositive(detailVO.getDelayDays())) {
            // 临时任务cronTask
            CronTask scheduleTask = xxlJobService.getCronTaskById(String.valueOf(cronTask.getXxlJobId()));
            //临时任务 xxljob
            XxlJobInfo jobInfo = xxlJobService.getXxlJobInfoById(scheduleTask.getXxlJobId());
            if(jobInfo.getTriggerStatus() == 0) {
                Date firstTaskTime = getFirstExecuteTimes(detailVO.getCronTask().getCron(), detailVO.getDelayDays());
                String executeTCron = CommonUtils.getCron(firstTaskTime);
                XxlJobHelper.log("[自救]jobInfo = {}, cronTask = {},detail = {}", JSON.toJSONString(jobInfo), JSON.toJSONString(cronTask), JSON.toJSONString(detailVO));
                xxlJobService.update(String.valueOf(cronTask.getXxlJobId()), executeTCron, cronTask.getMark(), "jobId=" + cronTask.getXxlJobId() + "的临时一次性任务", detailVO.getUpdateBy(), jobInfo.getExecutorHandler());
                xxlJobService.start(String.valueOf(cronTask.getXxlJobId()));
                XxlJobHelper.log("[自救]下一次临时一次性任务更新完成, 时间 = {}", executeTCron);
            }
        }
        XxlJobHelper.log("维保计划生成-执行结果 = {}", flag);
        return flag;
    }

    /**
     * 获取提前n天，第一个需要执行的任务（即提前n天，执行时间仍晚于当前时间的时间）
     * @param cron cron表达式
     * @return
     */
    private Date getFirstExecuteTimes(String cron, Double days) {
        int time = 2;
        // 下次执行的时间
        Date nextTime = null;
        while (nextTime == null || nextTime.compareTo(new Date()) <= days * 24 * 3600 * 1000 || time > 1024) {
            List<String> executeTimes = xxlJobService.nextTriggerTime(cron, time);
            Assert.notEmpty(executeTimes, "不合法的cron表达式,请确认定时任务设定的时间,保存后重试");
            for(int i = 0, size = executeTimes.size(); i < size; i++) {
                try {
                    nextTime = DateUtils.parseDate(executeTimes.get(i), "yyyy-MM-dd HH:mm:ss");
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
                Assert.notNull(nextTime, "非法日期字符串");
                if(nextTime.getTime() - days * 24 * 3600 * 1000 > (System.currentTimeMillis() + 5 * 1000)) {
                    /** fixup: 其中5秒为预读时间，因为xxl-job会将当前到之后5秒内的任务全部取出，例如当前是13:59:55，取出了13:59:55~14:00:00的任务
                     * 但获取下一次执行时间时，发现14:00:00在当前时间13:59:55之后，误认为14:00:00是下次执行的时间，其实14:00:00是这次执行的时间，
                     * 只是xxl-job预读了后5秒的数据，导致任务提前几秒执行了。应用将14:00:00作为下次执行的时间，但xxl-job在13:59:55执行的其实是14:00:00要执行的任务，
                     * 故在14:00:00时，不会再将任务执行一次，导致下次的临时任务时间失效
                     */
                    return new Date(nextTime.getTime() - (int)(days * 24 * 3600 * 1000));
                }
            }
            if(nextTime == null && executeTimes.size() == 1) {
                // 如果是定点任务， 且算上提前时间，任务执行的时间在当前时间之前
                throw new IllegalArgumentException("提前下达的时间已过，该任务将无法执行");
            }
            time <<= 1;
        }
        return null;
    }

    /**
     * 给vo的一些属性赋值
     * @param vo
     */
    private void voWrapper(MaintainPlanVO vo) {
        if(vo == null) {
            return;
        }
        if(vo instanceof MaintainPlanDetailVO && StringUtils.isNotBlank(vo.getId())) {
            MaintainPlanDetailVO detailVO = (MaintainPlanDetailVO)vo;
            detailVO.setCronTask(xxlJobService.getCronTaskById(vo.getId()));
            detailVO.setDispatch(dispatchConvertor.detailWrapper(detailVO.getDispatch()));
        }
    }

    /**
     * 创建日常维护工单
     */
    public MaintainDTO createMaintainOrder(MaintainPlanDetailVO detailVO, Integer taskType) {
        MaintainDTO dto = new MaintainDTO();
        dto.setDeptManage(detailVO.getDeptManage());
        dto.setDeptManageName(detailVO.getDeptManageName());
        dto.setMajor(detailVO.getMajor());
        dto.setNature(detailVO.getNature());
        dto.setSourceId(Constants.OrderSource.PLAN_ORDER);
        dto.setSource(Constants.OrderSource.orderSourceMap.get(dto.getSourceId()));
        // 工作地点id和名称
        dto.setLocation(detailVO.getAddressId());
        dto.setLocationName(detailVO.getAddress());
        // 计划开始时间和结束时间
        Date firstTime = null;
        if(Constants.YesOrNo.YES.equals(detailVO.getIsTimer())) {
            //如果是定时下发  即taskType=2时,此时获取的下一次触发时间是下一次,应该要取当次才行
            // 例如 每月1号 9点 工期2天,此时到了1月1号的九点,获取的下一次执行时间是 2月1号 实际期望 是此时
            if(taskType==2){
                firstTime = new Date();
            }else {
                firstTime = getFirstExecuteTimes(detailVO.getCronTask().getCron(), 0.0);
            }
            Assert.notNull(firstTime, "工单开始时间已过，请调整定时器后重试");
        }
        int days = CommonUtils.periodToHour(detailVO.getTimeNum(), TimePeriod.parse(detailVO.getTimePeriod()));
        Date[] orderTimes = CommonUtils.computeOrderTime(taskType, days, firstTime);
        // 设置标题生成规则
        dto.setTitle(detailVO.getDeptManageName() + detailVO.getContent());
        dto.setStartTime(orderTimes[0]);
        dto.setEndTime(orderTimes[1]);
        dto.setContext(detailVO.getContent());
        dto.setMarks(detailVO.getDesc());
        dto.setNeedStatus(detailVO.getIsNeed());
        // isCommit ? "待派工" : "草稿"
        dto.setIsCommit(false);
        dto.setCreateBy(detailVO.getCreateBy());
        dto.setCreateByName(detailVO.getCreateByName());
        dto.setCreateTime(new Date());
        dto.setUpdateBy(detailVO.getUpdateBy());
        dto.setUpdateByName(detailVO.getUpdateByName());
        dto.setUpdateTime(new Date());
        dto.setDeptId(detailVO.getDeptId());
        dto.setDeptName(detailVO.getDeptName());
        // 设置计划id和计划code
        dto.setPlanId(detailVO.getId());
        dto.setPlanCode(detailVO.getCode());
        dto.setMaintainReadyPoList(maintainConvertor.readysToReadyPos(detailVO.getWorkReadys()));
        dto.setMaintainEquipmentPoList(maintainConvertor.standardsToStandardPos(detailVO.getStandards()));
        dto.setOrderInstructionBeanList(maintainConvertor.instructsToInstructs(detailVO.getInstructions()));
        dto.setOrderEquipPoList(maintainConvertor.equipsToEquips(detailVO.getEquipments()));
        return dto;
    }


    /**
     * 工作流更新
     *
     * @param factory
     * @param redirectQuery
     */
    @Override
    @TaskAnnotation("workflowUpdate")
    public Boolean workflowUpdate(SessionFactory factory, RedirectQuery redirectQuery) {
        MaintainPlanMapper mapper = factory.getMapper(MaintainPlanMapper.class);
        Integer operation = redirectQuery.getOperation();
        MaintainPlanDetailVO vo = JSON.parseObject(redirectQuery.getFormVariables(), MaintainPlanDetailVO.class);
        switch (operation) {
            case 256: // 撤回
            case 1: // 驳回
                // 驳回的节点编号
                // 驳回到草稿节点
                vo.setStatus(Constants.MAINTAIN_PLAN_STATE.DRAFT);
                break;
            case 32: // 同意
                vo.setStatus(Constants.MAINTAIN_PLAN_STATE.WAIT_REVIEW);
                break;
        }
        vo.setDeleteFlag(false);
        mapper.saveOrUpdate(vo);
        return true;
    }
}
